package eliauk.fun.flowabledemo.config;

import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cmd.InjectUserTaskInProcessInstanceCmd;
import org.flowable.engine.impl.dynamic.BaseDynamicSubProcessInjectUtil;
import org.flowable.engine.impl.dynamic.DynamicUserTaskBuilder;
import org.flowable.engine.impl.persistence.entity.DeploymentEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class CustomInjectUserTaskCmd extends InjectUserTaskInProcessInstanceCmd {

    private final FlowElement currentElement;

    public CustomInjectUserTaskCmd(String processInstanceId, FlowElement currentElement, DynamicUserTaskBuilder dynamicUserTaskBuilder) {
        super(processInstanceId, dynamicUserTaskBuilder);
        this.currentElement = currentElement;
    }

    @Override
    protected void updateBpmnProcess(CommandContext commandContext, Process process, BpmnModel bpmnModel, ProcessDefinitionEntity originalProcessDefinitionEntity, DeploymentEntity newDeploymentEntity) {
        if (null == currentElement) {
            super.updateBpmnProcess(commandContext, process, bpmnModel, originalProcessDefinitionEntity, newDeploymentEntity);
            return;
        }
        if (!(this.currentElement instanceof UserTask)) {
            return;
        }
        StartEvent startEvent = getStartEvent(process);
        if (null == startEvent) {
            return;
        }
        UserTask currentUserTask = (UserTask) this.currentElement;
        if (currentUserTask.getOutgoingFlows().size() <= 0) {
            return;
        }
        SequenceFlow currentOutgoingFlow = currentUserTask.getOutgoingFlows().get(0);
        FlowElement targetFlowElement = currentOutgoingFlow.getTargetFlowElement();
        // 创建新的任务节点和两条连线
        UserTask newUserTask = createUserTask(process);
        SequenceFlow newSequenceFlow1 = createSequenceFlow(currentUserTask, newUserTask);
        SequenceFlow newSequenceFlow2 = createSequenceFlow(newUserTask, targetFlowElement);
        //加到流程内
        process.addFlowElement(newUserTask);
        process.addFlowElement(newSequenceFlow1);
        process.addFlowElement(newSequenceFlow2);
        process.removeFlowElement(currentOutgoingFlow.getId());

        //绘制新的流程图
        GraphicInfo elementGraphicInfo = bpmnModel.getGraphicInfo(currentUserTask.getId());
        if (elementGraphicInfo != null) {
            double yDiff = 0;
            double xDiff = 80;
            if (elementGraphicInfo.getY() < 173) {
                yDiff = 173 - elementGraphicInfo.getY();
                elementGraphicInfo.setY(173);
            }

            Map<String, GraphicInfo> locationMap = bpmnModel.getLocationMap();
            for (String locationId : locationMap.keySet()) {
                if (startEvent.getId().equals(locationId)) {
                    continue;
                }

                GraphicInfo locationGraphicInfo = locationMap.get(locationId);
                locationGraphicInfo.setX(locationGraphicInfo.getX() + xDiff);
                locationGraphicInfo.setY(locationGraphicInfo.getY() + yDiff);
            }

            Map<String, List<GraphicInfo>> flowLocationMap = bpmnModel.getFlowLocationMap();
            for (String flowId : flowLocationMap.keySet()) {
                List<GraphicInfo> flowGraphicInfoList = flowLocationMap.get(flowId);
                for (GraphicInfo flowGraphicInfo : flowGraphicInfoList) {
                    flowGraphicInfo.setX(flowGraphicInfo.getX() + xDiff);
                    flowGraphicInfo.setY(flowGraphicInfo.getY() + yDiff);
                }
            }
            //把之前的连线给移除了
            bpmnModel.removeFlowGraphicInfoList(currentOutgoingFlow.getId());
            //重新排版，这里需要引入flowable-bpmn-layout
            new BpmnAutoLayout(bpmnModel).execute();
        }

        BaseDynamicSubProcessInjectUtil.processFlowElements(commandContext, process, bpmnModel, originalProcessDefinitionEntity, newDeploymentEntity);
    }

    private SequenceFlow createSequenceFlow(UserTask currentUserTask, FlowElement newUserTask) {
        SequenceFlow sequenceFlow = new SequenceFlow();
        sequenceFlow.setId("newSequenceFlow_" + System.currentTimeMillis());
        sequenceFlow.setSourceRef(currentUserTask.getId());
        sequenceFlow.setSourceFlowElement(currentUserTask);
        sequenceFlow.setTargetFlowElement(newUserTask);
        sequenceFlow.setTargetRef(newUserTask.getId());
        return sequenceFlow;
    }

    private UserTask createUserTask(Process process) {
        UserTask newUserTask = new UserTask();
        // 生成唯一的ID
        newUserTask.setId(super.dynamicUserTaskBuilder.getId());
        newUserTask.setName(super.dynamicUserTaskBuilder.getName());
        // 设置其他属性，例如分配人、候选人等
        newUserTask.setAssignee(super.dynamicUserTaskBuilder.getAssignee());
        // 将新任务添加到流程中
        process.addFlowElement(newUserTask);
        return newUserTask;
    }

    private StartEvent getStartEvent(Process process) {
        Collection<FlowElement> flowElements = process.getFlowElements();
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof StartEvent) {
                return (StartEvent) flowElement;
            }
        }
        return null;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        return super.execute(commandContext);
    }

    @Override
    protected void updateExecutions(CommandContext commandContext, ProcessDefinitionEntity processDefinitionEntity, ExecutionEntity processInstance, List<ExecutionEntity> childExecutions) {

    }


}
